package org.flexspeedup.view
{
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.FontStyle;
import flash.text.TextFormat;
import flash.text.engine.FontLookup;
import flash.text.engine.FontPosture;
import flash.text.engine.FontWeight;

import flashx.textLayout.conversion.ConversionType;
import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.edit.EditManager;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.formats.TextLayoutFormat;

import mx.events.DragEvent;
import mx.events.FlexEvent;

import spark.components.Button;
import spark.components.TextArea;
import spark.components.TextInput;
import spark.components.supportClasses.SkinnableComponent;
import spark.events.TextOperationEvent;
import spark.utils.TextFlowUtil;

//------------------------------------------------------------------------------
//
//  Events
//
//------------------------------------------------------------------------------
[Event(name="change", type="spark.events.TextOperationEvent")]
[Event(name="formatOrLinkChange", type="flash.events.Event")]

//------------------------------------------------------------------------------
//
//  Skin States
//
//------------------------------------------------------------------------------

[SkinState("normal")]
[SkinState("createLink")]

/**
 * Enables simple text edition: 
 * - links 
 * - italics 
 * - bold
 */
public class TLFTextEditor extends SkinnableComponent
{
    //--------------------------------------------------------------------------
    //
    //  Class Constants
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  NORMAL
    //----------------------------------
    /**
     * @private
     */
    private static const NORMAL:String = "normal";

    //----------------------------------
    //  CREATE_LINK
    //----------------------------------
    /**
     * @private
     */
    private static const CREATE_LINK:String = "createLink";

    //----------------------------------
    //  FORMAT_OR_LINK_CHANGE
    //----------------------------------
    /**
     * Dispatched when the text format changes or a link has been applied
     */
    public static const FORMAT_OR_LINK_CHANGE:String = "formatOrLinkChange";
    
    //--------------------------------------------------------------------------
    //
    // Constructor
    //
    //--------------------------------------------------------------------------

    /**
     * Creates a new instance of this class.
     */
    public function TLFTextEditor()
    {
        super();
    }
    
    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  skinState
    //----------------------------------
    /**
     *  @private
     */
    private var skinState:String = NORMAL;
    
    //--------------------------------------------------------------------------
    //
    //  Skin Parts
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  applyLinkButton
    //----------------------------------
    [SkinPart(required="false")]
    public var applyLinkButton:Button;

    //----------------------------------
    //  cancelApplyLinkButton
    //----------------------------------
    [SkinPart(required="false")]
    public var cancelApplyLinkButton:Button;

    //----------------------------------
    //  createLinkButton
    //----------------------------------    
    [SkinPart(required="false")]
    public var createLinkButton:Button;

    //----------------------------------
    //  editor
    //----------------------------------
    [SkinPart(required="true")]
    public var editor:TextArea;
 
     //----------------------------------
     //  linkUrlInput
     //----------------------------------   
    [SkinPart(required="false")]
    public var linkUrlInput:TextInput;

    //----------------------------------
    //  toggleBoldFormatButton
    //----------------------------------
    [SkinPart(required="false")]
    public var toggleBoldFormatButton:Button;

    //----------------------------------
    //  toggleItalicFormatButton
    //----------------------------------
    [SkinPart(required="false")]
    public var toggleItalicFormatButton:Button;
    
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    // fontFamilyBold
    //----------------------------------
    /**
     * The name for the bold font face. Needs to be moved to a style..
     */
    public var fontFamilyBold:String = "bold";
   
    //----------------------------------
    // fontFamilyBoldItalic
    //----------------------------------
    /**
     * The name for the bold italic font face. Needs to be moved to a style.
     */
    public var fontFamilyBoldItalic:String = "bolditalic";

    //----------------------------------
    // fontFamilyItalic
    //----------------------------------
    /**
     * The name for the italic font face. Needs to be moved to a style.
     */
    public var fontFamilyItalic:String = "italic";
   
    //----------------------------------
    // fontFamilyNormal
    //----------------------------------
    /**
     * The name for the normal font face. Needs to be moved to a style.
     */
    public var fontFamilyNormal:String = "normal";
   
    //----------------------------------
    //  tlfString
    //----------------------------------
    /**
     * A string representation of the <code>TextFlow</code> instance in this 
     * component.
     */
    public function get tlfString():String
    {
        return TextConverter.export(
            editor.textFlow,
            TextConverter.TEXT_LAYOUT_FORMAT,
            ConversionType.STRING_TYPE).toString();
    }
    

    //----------------------------------
    //  text
    //----------------------------------
    /**
     *  @private
     */    
    private var _text:String;
    /**
     *  @private
     */
    private var textChanged:Boolean;   
    
    /**
     * The text being edited in this component. This is actually the 
     * <code>text</code> property in the <code>TextArea</code> in this component.
     */
    public function get text():String
    {
        return editor.text;
    }
    
    public function set text(value:String):void
    {
        if (value == _text)
            return;
            
        _text = value;
        textChanged = true;
        invalidateProperties();
    }

    //--------------------------------------------------------------------------
    //
    //  Overridden Methods - UIComponent
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();
        
        if (textChanged)
        {
            textChanged = false;
            
            if (editor)
                setText();
        }            
    }
    
    //--------------------------------------------------------------------------
    //
    //  Overridden Methods - SkinnableComponent
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function partAdded(partName:String, instance:Object):void
    {
        switch (instance)
        {
            case (applyLinkButton):
            {
                applyLinkButton.addEventListener(
                    MouseEvent.CLICK,
                    applyLinkButton_clickHandler
                );
                applyLinkButton.enabled = false;
                break;
            }
            case (cancelApplyLinkButton):
            {
                cancelApplyLinkButton.addEventListener(
                    MouseEvent.CLICK,
                    cancelApplyLinkButton_clickHandler
                );
            }
            case (createLinkButton):
            {
                createLinkButton.addEventListener(
                    MouseEvent.CLICK,
                    createLinkButton_clickHandler
                );
                createLinkButton.enabled = false;
                break;
            }
            case (editor):
            {
                editor.addEventListener(
                    FlexEvent.SELECTION_CHANGE, 
                    editor_selectionChangeHandler
                );
                editor.addEventListener(
                    TextOperationEvent.CHANGE, 
                    editor_changeHandler
                );
                setText();
                break;
            }
            case (linkUrlInput):
            {
                linkUrlInput.addEventListener(
                    TextOperationEvent.CHANGE,
                    linkUrlInput_changeHandler
                );
                linkUrlInput.addEventListener(
                    FlexEvent.ENTER,
                    linkUrlInput_enterHandler
                );
                break;
            }
            case (toggleBoldFormatButton):
            {
                toggleBoldFormatButton.addEventListener(
                    MouseEvent.CLICK,
                    toggleBoldFormatButton_clickHandler
                );
                break;
            }
            case (toggleItalicFormatButton):
            {
                toggleItalicFormatButton.addEventListener(
                    MouseEvent.CLICK,
                    toggleItalicFormatButton_clickHandler
                );
                break;
            }
            
        }
    }
    
    /**
     *  @private
     */
    override protected function partRemoved(partName:String, instance:Object):void
    {
        switch (instance)
        {
            case (applyLinkButton):
            {
                applyLinkButton.removeEventListener(
                    MouseEvent.CLICK,
                    applyLinkButton_clickHandler
                );
                break;
            }
            case (cancelApplyLinkButton):
            {
                cancelApplyLinkButton.removeEventListener(
                    MouseEvent.CLICK,
                    cancelApplyLinkButton_clickHandler
                );
            }
            case (createLinkButton):
            {
                editor.removeEventListener(
                    MouseEvent.CLICK,
                    createLinkButton_clickHandler
                );
                break;
            }
            case (editor):
            {
                editor.removeEventListener(
                    FlexEvent.SELECTION_CHANGE, 
                    editor_selectionChangeHandler
                );
                editor.removeEventListener(
                    TextOperationEvent.CHANGE, 
                    editor_changeHandler
                );
                break;
            }
            case (linkUrlInput):
            {
                linkUrlInput.addEventListener(
                    TextOperationEvent.CHANGE,
                    linkUrlInput_changeHandler
                );
                linkUrlInput.removeEventListener(
                    FlexEvent.ENTER,
                    linkUrlInput_enterHandler
                );
                break;
            }
            case (toggleBoldFormatButton):
            {
                toggleBoldFormatButton.removeEventListener(
                    MouseEvent.CLICK,
                    toggleBoldFormatButton_clickHandler
                );
                break;
            }
            case (toggleItalicFormatButton):
            {
                toggleItalicFormatButton.removeEventListener(
                    MouseEvent.CLICK,
                    toggleItalicFormatButton_clickHandler
                );
                break;
            }
        }
    } 
    
    /**
     *  @private
     */
    override protected function getCurrentSkinState():String
    {
        return skinState;
    }
    
    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  applyLink
    //----------------------------------
    /**
     * @private
     */
    private function applyLink():void
    {
        EditManager(editor.textFlow.interactionManager).applyLink(linkUrlInput.text);
        notifyFormatOrLinkChange();
        finishApplyLink();
    } 

    //----------------------------------
    //  cancelApplyLink
    //----------------------------------
    /**
     * @private
     */
    private function cancelApplyLink():void
    {
        finishApplyLink();
    } 

    //----------------------------------
    //  finishApplyLink
    //----------------------------------
    /**
     * @private
     */
    private function finishApplyLink():void
    {
        linkUrlInput.text = "";
        applyLinkButton.enabled = false;
        reset();
    } 

    //----------------------------------
    //  createLink
    //----------------------------------
    /**
     * @private
     */
    private function createLink():void
    {
        skinState = CREATE_LINK; 
        invalidateSkinState();
    }     
    
    //----------------------------------
    //  getSelectionFormat
    //----------------------------------
    /**
     * @private
     */
    private function getSelectionFormat():TextLayoutFormat
    {
        return editor.getFormatOfRange(
            null,
            editor.selectionAnchorPosition,
            editor.selectionActivePosition
        );
    } 

    //----------------------------------
    //  setSelectionFormat
    //----------------------------------
    /**
     * @private
     */
    private function setSelectionFormat(format:TextLayoutFormat):void
    {
        //  find out font face
        if (format.fontWeight == FontWeight.BOLD && format.fontStyle == FontPosture.ITALIC)
            format.fontFamily = fontFamilyBoldItalic;
        else if (format.fontWeight == FontWeight.BOLD)
            format.fontFamily = fontFamilyBold;
        else if (format.fontStyle == FontPosture.ITALIC)
            format.fontFamily = fontFamilyItalic;
        else
            format.fontFamily = fontFamilyNormal;
                
        editor.setFormatOfRange(
            format,
            editor.selectionAnchorPosition,
            editor.selectionActivePosition
        );

        editor.setFocus();    
    } 
    
    //----------------------------------
    //  reset
    //----------------------------------
    /**
     * @private
     */
    private function reset():void
    {
        skinState = NORMAL;
        invalidateSkinState();    
    } 
    
    //----------------------------------
    //  setText
    //----------------------------------
    /**
     * @private
     */
    private function setText():void
    {
        if (!_text)
        {
            editor.text = "";
            return;
        }
            
        var textFlow:TextFlow;

        try
        {
            textFlow = TextFlowUtil.importFromString(_text);
        }
        catch (error:Error)
        {
        }
        
        if (textFlow)
            editor.textFlow = textFlow;
        else
            editor.text = _text;
    } 

    //----------------------------------
    //  toggleBoldFormat
    //----------------------------------
    /**
     * @private
     */
    private function toggleBoldFormat():void
    {
        notifyFormatOrLinkChange();
        var format:TextLayoutFormat = getSelectionFormat();
        format.fontWeight = (format.fontWeight == FontWeight.BOLD) ? 
            FontWeight.NORMAL : FontWeight.BOLD;
        setSelectionFormat(format);
    } 

    //----------------------------------
    //  toggleItalicFormat
    //----------------------------------
    /**
     * @private
     */
    private function toggleItalicFormat():void
    {
        notifyFormatOrLinkChange();
        var format:TextLayoutFormat = getSelectionFormat();
        format.fontStyle = (format.fontStyle == FontPosture.ITALIC) ?
            FontPosture.NORMAL : FontPosture.ITALIC;
        setSelectionFormat(format);
    } 

    //----------------------------------
    //  updateApplyLinkButton
    //----------------------------------
    /**
     * @private
     */
    private function updateApplyLinkgButton():void
    {
        if (linkUrlInput.text.length > 0)
            applyLinkButton.enabled = true;
        else
            applyLinkButton.enabled = false;    
    } 
    
    //----------------------------------
    //  updateCreateLinkButton
    //----------------------------------
    /**
     * @private
     */
    private function updateCreateLinkButton():void
    {
        if (editor.selectionAnchorPosition == editor.selectionActivePosition)
            createLinkButton.enabled = false;
        else
            createLinkButton.enabled = true;    
    }

    //----------------------------------
    //  notifyFormatOrLinkChange
    //----------------------------------
    /**
     * @private
     */
    private function notifyFormatOrLinkChange():void
    {
        dispatchEvent(new Event(FORMAT_OR_LINK_CHANGE));    
    } 

    //--------------------------------------------------------------------------
    //
    //  Event Handlers
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  editor_changeHandler
    //----------------------------------
    /**
     * @private
     */
    private function editor_changeHandler(event:TextOperationEvent):void
    {
        dispatchEvent(event.clone());
    }
    
    //----------------------------------
    //  editor_selectionChangeHandler
    //----------------------------------
    /**
     * @private
     */
    private function editor_selectionChangeHandler(event:FlexEvent):void
    {
        updateCreateLinkButton();
    }

    //----------------------------------
    //  applyLinkButton_clickHandler
    //----------------------------------
    /**
     * @private
     */
    private function applyLinkButton_clickHandler(event:MouseEvent):void
    {
        applyLink();
    }

    //----------------------------------
    //  cancelApplyLinkButton_clickHandler
    //----------------------------------
    /**
     * @private
     */
    private function cancelApplyLinkButton_clickHandler(event:MouseEvent):void
    {
        finishApplyLink();
    }    
  
    //----------------------------------
    //  createLinkButton_clickHandler
    //----------------------------------
    /**
     * @private
     */
    private function createLinkButton_clickHandler(event:MouseEvent):void
    {
        createLink();
    } 

    //----------------------------------
    //  linkUrlInput_changeHandler
    //----------------------------------
    /**
     * @private
     */
    private function linkUrlInput_changeHandler(event:TextOperationEvent):void
    {
        updateApplyLinkgButton();
    }
    
    //----------------------------------
    //  linkUrlInput_enterHandler
    //----------------------------------
    /**
     * @private
     */
    private function linkUrlInput_enterHandler(event:FlexEvent):void
    {
        applyLink();
    }
    
    //----------------------------------
    //  toggleBoldFormatButton_clickHandler
    //----------------------------------
    /**
     * @private
     */
    private function toggleBoldFormatButton_clickHandler(event:MouseEvent):void
    {
        toggleBoldFormat();
    }
    
    //----------------------------------
    //  toggleItalicFormatButton_clickHandler
    //----------------------------------
    /**
     * @private
     */
    private function toggleItalicFormatButton_clickHandler(event:MouseEvent):void
    {
        toggleItalicFormat();
    }

}
}